// Copyright (c) XiGuan 2023
// Author： xiguan
// Email: xiguan.teng@qq.com
// Create on 2023/11/14
// TODO:
//

#include "HeapTimer.h"

#include <assert.h>

HeapTimer::HeapTimer() { heap_.resize(64); }

HeapTimer::~HeapTimer() { clear(); }

void HeapTimer::clear() {
  ref_.clear();
  heap_.clear();
}

void HeapTimer::swap_node_(size_t idx, size_t jdx) {
  assert(idx >= 0 && idx < heap_.size());
  assert(jdx >= 0 && jdx < heap_.size());

  std::swap(heap_[idx], heap_[jdx]);
  ref_[heap_[idx].id] = idx;
  ref_[heap_[jdx].id] = jdx;
}

void HeapTimer::sift_up_(size_t idx) {
  assert(idx >= 0 && idx < heap_.size());
  size_t jdx = (idx - 1) / 2;  // 自己的父节点

  while (jdx >= 0) {
    if (heap_[jdx] < heap_[idx]) {
      break;
    }
    swap_node_(idx, jdx);
    idx = jdx;
    jdx = (idx - 1) / 2;
  }
}

bool HeapTimer::sift_down_(size_t idx, size_t n) {
  assert(idx >= 0 && idx < heap_.size());
  assert(n >= 0 && n <= heap_.size());

  size_t i = idx;
  size_t j = i * 2 + 1;  // 左子树

  while (j < n) {
    // 选出左右子树最小的结点
    if (j + 1 < n && heap_[j + 1] < heap_[j]) {
      j++;
    }
    if (heap_[i] < heap_[j]) {
      break;
    }
    swap_node_(i, j);
    i = j;
    j = i * 2 + 1;
  }

  return i > idx;
}

void HeapTimer::add(int id, int timeout,
                    const TimeoutCallBack &timeout_callback) {
  assert(id >= 0);
  size_t idx = -1;

  if (ref_.count(id) == 0) {
    /** 新结点: 堆尾插入, 调整堆 */
    idx = heap_.size();
    ref_[id] = idx;
    heap_.push_back({id, Clock::now() + MS(timeout), timeout_callback});
    sift_up_(idx);
  } else {
    /** 已经有节点了, 调整堆 */
    idx = ref_[id];
    heap_[idx].expires = Clock::now() + MS(timeout); // 超时时间点
    heap_[idx].timeout_callback = timeout_callback;
    if (sift_down_(idx, heap_.size())) {
      sift_up_(idx);
    }
  }
}

void HeapTimer::adjust(int id, int new_expires) {
  assert(!heap_.empty() && ref_.count(id) > 0);
  heap_[ref_[id]].expires = Clock::now() + MS(new_expires);
  sift_down_(ref_[id], heap_.size());
}

void HeapTimer::del_(size_t idx) {
  assert(!heap_.empty() && idx >= 0 && idx < heap_.size());
  size_t i = idx;
  size_t n = heap_.size() - 1;

  if (i < n) {
    swap_node_(i, n);
    if (!sift_down_(i, n)) {
      sift_up_(i);
    }
  }

  ref_.erase(heap_.back().id);
  heap_.pop_back();
}

void HeapTimer::do_work(int id) {
  if (heap_.empty() || ref_.count(id) == 0) {
    return;
  }
  size_t idx = ref_[id];
  TimerNode timerNode = heap_[idx];
  timerNode.timeout_callback();
  del_(idx);
}

void HeapTimer::tick() {
  if (heap_.empty()) {
    return;
  }

  while (!heap_.empty()) {
    TimerNode node = heap_.front();
    if (std::chrono::duration_cast<MS>(node.expires - Clock::now()).count() >
        0) {
      break;
    }
    node.timeout_callback();
    pop();
  }
}

void HeapTimer::pop() {
  assert(!heap_.empty());
  del_(0);
}

int HeapTimer::get_next_tick() {
  tick();
  auto res = -1;

  if (!heap_.empty()) {
    res = std::chrono::duration_cast<MS>(heap_.front().expires - Clock::now())
              .count();
  }
  return res;
}
